<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>迭代器模式</title>
</head>
<body>
<script>
    /**
     * 迭代器模式就是提供一种方法顺序地访问对象中的元素
     * 迭代器模式可以把迭代的过程从业务逻辑中分离出来
     * */

    // 实现自己的迭代器
    function each(arr, callback) {
        for (let i = 0; i < arr.length; i++) {
            callback.call(arr[i], arr[i], i)
        }
    }

    each([1, 2, 3], function (n, i) {
        console.log(n, i)
    })


    /**
     * 迭代器分为内部迭代器和外部迭代器，上面的each就是内部迭代器
     * 内部迭代器定义好了迭代规则，它完全接手整个迭代过程，外部只需一次初始调用
     * 缺点是不够灵活，如each函数不能同时迭代两个数组
     *
     * 外部迭代器必须显示地请求迭代下一个元素
     * 外部迭代器增加了一些调用的复杂度，但也增强了迭代器的灵活性
     * 可手工控制迭代的过程或顺序
     * 外部迭代器虽然调用复杂但适用面更广，能满足更多变的需求
     * 两种迭代器在实际生产中无优劣之分，看需求场景
     * */
    const Iterator = function (obj) {
        let current = 0

        function next() {
            current += 1
        }

        function isDone() {
            return current >= obj.length
        }

        function getCurrentItem() {
            return obj[current]
        }

        return {
            next,
            isDone,
            getCurrentItem
        }
    }

    function compare(iterator1, iterator2) {
        while (!iterator1.isDone() && !iterator2.isDone()) {
            if (iterator1.getCurrentItem() !== iterator2.getCurrentItem()) {
                throw new Error('iterator1和iterator2不相等')
            }
            iterator1.next()
            iterator2.next()
        }
        alert('iterator1和iterator2相等')
    }

    const iterator1 = Iterator([1, 2, 3])
    const iterator2 = Iterator([1, 2, 3])
    compare(iterator1, iterator2)


    /**
     * 迭代器模式不仅可以迭代数组也可迭代类数组对象，只要对象有length属性并且可通过索引访问
     *
     * 可实现倒序的迭代器
     *
     * 中止迭代器，通过return false来中止循环
     * */
    function each(arr, callback) {
        for (let i = 0; i < arr.length; i++) {
            if (callback.call(arr[i], arr[i], i) === false) {
                break
            }
        }
    }


    /**
     * 迭代器的应用实例
     * 实现不同浏览器的文件上传，不同浏览器下上传方式不一样
     * 好比有一串钥匙和3把锁，不知道哪把钥匙能开哪把锁
     * 于是从第一把锁开始，迭代钥匙进行尝试，直到找到正确钥匙为止
     * */
    function getActiveUploadObj() {
        // IE上传控件
        try {
            return new ActiveXObject('TXFTNActiveX.FTNUpload')
        } catch (e) {
            return false
        }
    }

    function getFlashUploadObj() {
        // flash进行上传，略
    }

    function getFormUploadObj() {
        // 表单进行上传，略
    }

    function iteratorUploadObj() {
        for (let i = 0, fn; fn = arguments[i++];) {
            const uploadObj = fn()
            if (uploadObj !== false) return uploadObj
        }
    }

    // 按优先级添加到迭代器
    const uploadObj = iteratorUploadObj(getActiveUploadObj, getFormUploadObj, getFormUploadObj)
</script>
</body>
</html>